package entities;

import java.util.HashMap;
import java.util.HashSet;

import datatableaux.DataCompletion;
import datatableaux.rules.DataAllRule;
import datatableaux.rules.DataAndRule;
import datatableaux.rules.DataOrRule;
import datatableaux.rules.DataSomeRule;
import datatableaux.rules.DataUnfoldingRule;


import entities.concept.Concept;
import entities.concept.UniversalConcept;
import entities.role.Role;
import entities.vocabulary.KBVocabulary;

/**
 * Note that UNA is assumed, i.e., distinct individual names suggest different objects.
 * Plus, all individuals are assumed to be ordered for implementing blocking.
 * 
 * Blocking for Inverse roles (I), cf. a DL with transitive and inverse roles and role hierarchies by Ian and Ulrike. Sec. 2
 * Dynamic blocking: remember this individual is blocked by whom --> to be broken later
 * Non-generating rule: still applicable to blocked nodes
 * Non-blockable nodes: only for nominals
 * 
 * @author j55wu
 *
 */

public class Individual implements Comparable<Individual>{
	private String name;
	private int order;
	private static final String anonIdPrefix = "Anonymous_Ind_";
	private static int anonIdCount = 0;
	private static HashMap<String, Individual> allIndividuals
		= new HashMap<String, Individual>();


	////////
	private HashMap<Role, HashSet<Individual>> successors;
	private HashMap<Role, HashSet<Individual>> ancestors;
	private HashSet<Concept> labels;
	private HashSet<Concept> expandedLabels;
	private HashSet<Concept> unfoldedLabels;
	private HashSet<HashSet<Concept>> binaryExpanded;
	////////
	public boolean labelAddedChange;
	public boolean isSat;
	
	///////////////////////////////
	private void init(){
		this.order = anonIdCount ++;
		Individual.allIndividuals.put(this.name, this);
		this.successors = new HashMap<Role, HashSet<Individual>>();
		this.ancestors = new HashMap<Role, HashSet<Individual>>();
		this.labels = new HashSet<Concept>();
		this.expandedLabels = new HashSet<Concept>();
		this.unfoldedLabels = new HashSet<Concept>();
		this.binaryExpanded = new HashSet<HashSet<Concept>>();
		this.isSat = true;
		this.labelAddedChange = false;
	}
	
	public void restore(){
//		this.successors = new HashMap<Role, HashSet<Individual>>();
//		this.ancestors = new HashMap<Role, HashSet<Individual>>();
//		this.labels = new HashSet<Concept>();
//		this.expandedLabels = new HashSet<Concept>();
//		this.unfoldedLabels = new HashSet<Concept>();
//		this.binaryExpanded = new HashSet<HashSet<Concept>>();
		this.successors.clear();
		this.ancestors.clear();
		this.labels.clear();
		this.expandedLabels.clear();
		this.unfoldedLabels.clear();
		this.binaryExpanded.clear();
		this.isSat = true;
		this.labelAddedChange = false;
	}
	
	public String getName(){
		return this.name;
	}
	
	public static void clearAllIndividuals(){
		//must clear all individuals for each new reasoning service
		Individual.allIndividuals.clear();
	}
	
	//instance controlled
	public static Individual newIndividual(String name){
		if(Individual.allIndividuals != null && Individual.allIndividuals.containsKey(name)){
			return Individual.allIndividuals.get(name);
		}
		return new Individual(name);
	}
	
	public static Individual getStarIndividual(){
		Individual ind = new Individual(KBVocabulary.SPECIAL_IND_SYMB);
		//this individual is not supposed to be reasoned with.
		Individual.allIndividuals.remove(KBVocabulary.SPECIAL_IND_SYMB);
		return ind;
	}
	public static Individual getAnonIndividual() {
		return new Individual();
	}

	private Individual(String name){
		this.name = name;
		init();
	}
	
	//anonymous inds
	private Individual(){
		this.name = anonIdPrefix + anonIdCount;
		init();
	}
	
	public static HashMap<String, Individual> getAllIndividuals() {
		return allIndividuals;
	}
	public int getOrder(){
		return this.order;
	}
	
	public boolean blocked (){
		boolean ret = false;
//		for(Individual n: this.getAncestors()){
//			ret |= n.labels.containsAll(this.labels) || !n.isSat;
//		}
		for(Individual n: this.getAncestors()){
			ret |=  !n.isSat;
		}
		return ret;
	}
	
	public boolean blockedGeneratingRule(){
		boolean ret = false;
		for(Individual n: this.getAncestors()){
			ret |= n.labels.containsAll(this.labels);
		}
		return ret;
	}
	
	public boolean isSameIndividual(Individual b){
		return b.name.compareTo(this.name) == 0;
	}
	
	public HashSet<Concept> getUnfolededLabels(){
		return this.unfoldedLabels;
	}
	
	public void addUnfoldedLabels(Concept c){
		this.unfoldedLabels.add(c);
	}
	
	public  HashSet<HashSet<Concept>> getBinaryExpanded(){
		return this.binaryExpanded;
	}
	
	public boolean addBinaryExpanded(HashSet<Concept> bs){
		return this.binaryExpanded.add(bs);
	}
	
	public void removeLabels(Concept c) {
		this.labels.remove(c);
	}
	public void removeLabels(HashSet<Concept> cs) {
		this.labels.removeAll(cs);
	}
	
	
	public void addLabels(Concept c){
		if(!this.labels.contains(c)){
			this.labels.add(c);
			//System.out.println("adding: "+this+" : " + c);
			this.labelAddedChange = true;
		}
		
	}
	
	public void addLabels(HashSet<Concept> cs){
		for(Concept c:cs){
			this.addLabels(c);
		}
		
	}
	
	public HashSet<Concept> getLabels(){
		return this.labels;
	}
	
	public HashSet<Concept> getExpandedLabels(){
		return this.expandedLabels;
	}
	
	public boolean addExpandedLabels(Concept c){
		return this.expandedLabels.add(c);
	}
	public boolean addExpandedLabels(HashSet<Concept> c){
		return this.expandedLabels.addAll(c);
	}
	
	////////////////////////////////////////////////////////
	public boolean hasThisAncestor(Individual anc){
		boolean ret = false;
//		if(this.equals(anc)){
//			return true;
//		}
		for(Individual n:this.getAncestors()){
			ret |= n.equals(anc) || n.hasThisAncestor(anc);
		}
		return ret;
	}
	
//	private void addAncestors(Role r, HashSet<Individual> ns){
//		if(this.ancestors.containsKey(r)){
//			this.ancestors.get(r).addAll(ns);
//		}else{
//			this.ancestors.put(r, ns);
//		}
//	}
	
	private void addAncestors(Role r, Individual ns){
		if(this.ancestors.containsKey(r)){
			this.ancestors.get(r).add(ns);
		}else{
			HashSet<Individual> nss = new HashSet<Individual>();
			nss.add(ns);
			this.ancestors.put(r, nss);
		}
	}
	
	private void removeAncestors(Role r, Individual ns){
		if(this.ancestors.containsKey(r)){
			this.ancestors.get(r).remove(ns);
			if(this.ancestors.get(r).size()<1){
				this.ancestors.remove(r);
			}
		}
	}
	
//	private void removeAncestors(Role r, HashSet<Individual> ns){
//		if(this.ancestors.containsKey(r)){
//			this.ancestors.get(r).removeAll(ns);
//			if(this.ancestors.get(r).size()<1){
//				this.ancestors.remove(r);
//			}
//		}
//	}
	

	public HashSet<Individual> getAncestors(Role r){
		if(this.ancestors == null){
			return null;
		}
		if(this.ancestors.containsKey(r)){
			return this.ancestors.get(r);
		}
		return null;
	}
	
	/*
	 * Get all successors with all roles
	 */
	public HashSet<Individual> getAncestors(){
		if(this.ancestors== null){
			return null;
		}
		HashSet<Individual> ret = new HashSet<Individual>();
		for(Role r:this.ancestors.keySet()){
			if(this.ancestors.containsKey(r)){
				ret.addAll( this.ancestors.get(r) );
			}
		}
		
		return ret;
	}
	
	public HashMap<Role, HashSet<Individual>> returnAncestors(){
		return this.ancestors;
	}
	
	/////////////////////////
	public void addSucessors(Role r, HashSet<Individual> ns){
		if(this.successors.containsKey(r)){
			this.successors.get(r).addAll(ns);
		}else{
			this.successors.put(r, ns);
		}
		//add this as ancestor for ns
		for(Individual ind:ns){
			ind.addAncestors(r, this);
		}
	}
	
	public void removeSucessors(Role r, Individual ns){
		if(this.successors.containsKey(r)){
			this.successors.get(r).remove(ns);
			if(this.successors.get(r).size()<1){
				this.successors.remove(r);
			}
			//now remove this from their ancestors
			ns.removeAncestors(r, this);
		}
	}
	
	public void removeSucessors(Role r, HashSet<Individual> ns){
		if(this.successors.containsKey(r)){
			this.successors.get(r).remove(ns);
			if(this.successors.get(r).size()<1){
				this.successors.remove(r);
			}
			//now remove this from their ancestors
			for(Individual n:ns){
				n.removeAncestors(r, this);
			}			
		}
	}
	
	public void addSucessors(Role r, Individual ns){
		if(this.successors.containsKey(r)){
			this.successors.get(r).add(ns);
		}else{
			HashSet<Individual> nss = new HashSet<Individual>();
			nss.add(ns);
			this.successors.put(r, nss);
		}
		//add this as an ancestor of ns
		ns.addAncestors(r, this);
	}

	public HashSet<Individual> getSuccessors(Role r){
		if(this.successors == null){
			return null;
		}
		if(this.successors.containsKey(r)){
			return this.successors.get(r);
		}
		return null;
	}
	
	/*
	 * Get all successors with all roles
	 */
	public HashSet<Individual> getSuccessors(){
		if(this.successors == null){
			return null;
		}
		HashSet<Individual> ret = new HashSet<Individual>();
		for(Role r:this.successors.keySet()){
			if(this.successors.containsKey(r)){
				ret.addAll( this.successors.get(r) );
			}
		}
		
		return ret;
	}
	
	public HashMap<Role, HashSet<Individual>> returnSuccessors(){
		return this.successors;
	}
	
	// restore a certain type of concepts to unexpanded labels
	public boolean restoreForallConceptToExpandedLabels(Role r){
		boolean done = false;
		for(Concept c: this.getLabels()){
			if(c instanceof UniversalConcept){
				//test if that role is this role
				UniversalConcept rc = (UniversalConcept)c;
				if(rc.getRole().equals(r)){
					done |= true;
					if(this.getExpandedLabels().contains(rc)){
						this.getExpandedLabels().remove(rc);
					}
				}
			}
		}
		return done;
	}
	////////////////////////////////////////////////////////////////////
	
	@Override
	public String toString(){
		return this.name;
	}
	
	public Individual getPrimeCopy() {
		return new Individual(this.name + entities.concept.Concept.PRIME);
	}
	
	@Override
	public int hashCode(){
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((this.name == null) ? 0 : this.name.hashCode());
		return result;
	}
	

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		//System.out.println(obj.toString()+this.name);
		if (!(obj instanceof Individual)){
			//System.out.println("first false:"+obj.toString()+this.name);
			return false;
		}
		Individual other = (Individual) obj;
		if (this.name == null) {
			if (other.name != null){
				//System.out.println("second false:"+obj.toString()+this.name);
				return false;
			}
		} else if (!this.name.equals(other.name)){
			//System.out.println("3rd false:"+obj.toString()+this.name);
			return false;
		}
		return true;
	}

	public boolean isCompleted() {
		return this.expandedLabels.containsAll(this.labels);
	}
	
	public boolean anyMoreRules(DataCompletion dc){
		DataAndRule cr = new DataAndRule(dc);
		DataOrRule dr = new DataOrRule(dc);
		DataSomeRule er = new DataSomeRule(dc);
		DataAllRule fr = new DataAllRule(dc);
		DataUnfoldingRule ar = new DataUnfoldingRule(dc);
		if(!cr.isApplicable(this) &&
				!dr.isApplicable(this)&&
				!er.isApplicable(this)&&
				!fr.isApplicable(this)&&
				!ar.isApplicable(this)){
			return false;
		}
		return true;
	}

	public void addBinaryToExpandedLabels() {
		if(this.getBinaryExpanded().size()<1){
			//nothing to add
			return;
		}else{
			for(HashSet<Concept> cc: this.getBinaryExpanded()){
				this.addExpandedLabels(cc);
			}
		}
		
	}

	public int compareTo(Individual o) {
		// TODO Auto-generated method stub
		return this.order - o.order;
	}


	
}
